package DyingBaby.backend.common;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * LRU缓存实现类
 * 使用双向链表+HashMap实现LRU（Least Recently Used）策略
 */
public abstract class LRUCache<T> {
    // LRU节点类
    private static class LRUNode<T> {
        Long key;
        T value;
        LRUNode<T> prev;
        LRUNode<T> next;
        
        LRUNode(Long key, T value) {
            this.key = key;
            this.value = value;
        }
    }
    
    private HashMap<Long, T> cache;                     // 实际缓存的数据
    private HashMap<Long, LRUNode<T>> nodeMap;          // key到节点的映射
    private HashMap<Long, Boolean> getting;             // 正在被获取的资源
    
    private LRUNode<T> head;                            // 双向链表头节点（最近使用）
    private LRUNode<T> tail;                            // 双向链表尾节点（最久未使用）
    
    private int maxResource;                            // 缓存的最大缓存资源数
    private int count = 0;                              // 缓存中元素的个数
    private Lock lock;
    
    // 缓存统计信息
    private long hitCount = 0;                          // 缓存命中次数
    private long missCount = 0;                         // 缓存未命中次数
    private long totalRequests = 0;                     // 总请求次数
    private long evictionCount = 0;                     // 缓存驱逐次数

    public LRUCache(int maxResource) {
        this.maxResource = maxResource;    
        cache = new HashMap<>();
        nodeMap = new HashMap<>();
        getting = new HashMap<>();
        lock = new ReentrantLock();
        
        // 初始化双向链表
        head = new LRUNode<>(null, null);
        tail = new LRUNode<>(null, null);
        head.next = tail;
        tail.prev = head;
    }

    protected T get(long key) throws Exception {
        if (CacheConfig.isStatsEnabled()) {
            totalRequests++; // 增加总请求次数
        }
        
        while(true) {
            lock.lock();
            if(getting.containsKey(key)) {
                // 请求的资源正在被其他线程获取
                lock.unlock();
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    continue;
                }
                continue;
            }

           // 资源在缓存中，直接返回，同时更新LRU顺序
            if(cache.containsKey(key)) {
                T obj = cache.get(key);
                moveToHead(key); // 将节点移动到头部（标记为最近使用）
                if (CacheConfig.isStatsEnabled()) {
                    hitCount++; // 增加命中次数
                }
                lock.unlock();
                return obj;
            }

            // 如果资源不在缓存中，先判断是否已经达到设定的最大缓存资源数
            // 如果达到，需要驱逐最久未使用的资源
            if(maxResource > 0 && count == maxResource) {
                evictLRU(); // 驱逐最久未使用的资源
            }
            
            if (CacheConfig.isStatsEnabled()) {
                missCount++; // 增加未命中次数
            }
            count++;
            getting.put(key, true);
            lock.unlock();
            break;
        }

        T obj = null;
        try {
            // 从数据源获取资源是相对费时的操作
            obj = getForCache(key);
        } catch(Exception e) {
            lock.lock();
            count--;
            getting.remove(key);
            lock.unlock();
            throw e;
        }

        lock.lock();
        getting.remove(key);
        cache.put(key, obj);
        addToHead(key, obj); // 将新节点添加到头部
        lock.unlock();
        return obj;
    }

    /**
     * 强行释放一个缓存，如果发现引用数为0，回源并删除缓存中所有相关的结构
     */
    protected void release(long key) {
        lock.lock();
        try {
            if(cache.containsKey(key)) {
                T obj = cache.get(key);
                releaseForCache(obj);
                removeNode(key);
                cache.remove(key);
                count--;
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 关闭缓存，写回所有资源。遍历缓存中的所有key，逐一删除
     */
    protected void close() {
        lock.lock();
        try {
            Set<Long> keys = cache.keySet();
            for (long key : keys) {
                T obj = cache.get(key);
                releaseForCache(obj);
                removeNode(key);
                cache.remove(key);
            }
            count = 0;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 将节点移动到链表头部（标记为最近使用）
     */
    private void moveToHead(long key) {
        LRUNode<T> node = nodeMap.get(key);
        if (node != null) {
            // 从当前位置移除
            node.prev.next = node.next;
            node.next.prev = node.prev;
            
            // 添加到头部
            addToHead(key, node.value);
        }
    }

    /**
     * 将节点添加到链表头部
     */
    private void addToHead(long key, T value) {
        LRUNode<T> newNode = new LRUNode<>(key, value);
        
        // 插入到头部
        newNode.next = head.next;
        newNode.prev = head;
        head.next.prev = newNode;
        head.next = newNode;
        
        nodeMap.put(key, newNode);
    }

    /**
     * 移除指定节点
     */
    private void removeNode(long key) {
        LRUNode<T> node = nodeMap.get(key);
        if (node != null) {
            node.prev.next = node.next;
            node.next.prev = node.prev;
            nodeMap.remove(key);
        }
    }

    /**
     * 驱逐最久未使用的资源（链表尾部）
     */
    private void evictLRU() {
        if (tail.prev != head) {
            LRUNode<T> lastNode = tail.prev;
            long keyToEvict = lastNode.key;
            
            // 从缓存中移除
            T obj = cache.get(keyToEvict);
            releaseForCache(obj);
            cache.remove(keyToEvict);
            removeNode(keyToEvict);
            count--;
            
            if (CacheConfig.isStatsEnabled()) {
                evictionCount++; // 增加驱逐次数
            }
        }
    }

    /**
     * 当资源不在缓存时的获取行为
     */
    protected abstract T getForCache(long key) throws Exception;
    
    /**
     * 当资源被驱逐时的写回行为
     */
    protected abstract void releaseForCache(T obj);
    
    /**
     * 获取缓存命中次数
     */
    public long getHitCount() {
        return hitCount;
    }
    
    /**
     * 获取缓存未命中次数
     */
    public long getMissCount() {
        return missCount;
    }
    
    /**
     * 获取总请求次数
     */
    public long getTotalRequests() {
        return totalRequests;
    }
    
    /**
     * 获取缓存驱逐次数
     */
    public long getEvictionCount() {
        return evictionCount;
    }
    
    /**
     * 获取缓存命中率
     */
    public double getHitRate() {
        if (totalRequests == 0) {
            return 0.0;
        }
        return (double) hitCount / totalRequests;
    }
    
    /**
     * 获取缓存统计信息字符串
     */
    public String getCacheStats() {
        return String.format("LRU缓存统计 - 总请求: %d, 命中: %d, 未命中: %d, 驱逐: %d, 命中率: %.2f%%, 当前缓存大小: %d/%d", 
                totalRequests, hitCount, missCount, evictionCount, getHitRate() * 100, count, maxResource);
    }
    
    /**
     * 重置缓存统计信息
     */
    public void resetStats() {
        lock.lock();
        try {
            hitCount = 0;
            missCount = 0;
            totalRequests = 0;
            evictionCount = 0;
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 获取当前缓存元素个数
     */
    protected int getCurrentCount() {
        return count;
    }
    
    /**
     * 获取最大缓存资源数
     */
    protected int getMaxResource() {
        return maxResource;
    }
    
    /**
     * 获取LRU链表的当前状态（用于调试）
     */
    public String getLRUChainStatus() {
        StringBuilder sb = new StringBuilder();
        sb.append("LRU链: ");
        LRUNode<T> current = head.next;
        while (current != tail) {
            sb.append(current.key).append(" -> ");
            current = current.next;
        }
        sb.append("null");
        return sb.toString();
    }
}
